<template>
  <div ref="container" class="model-container">
    <div v-if="loading" class="loading-overlay">
      <div class="loading-content">
        <div class="spinner"></div>
        <div>加载中... {{ progress }}%</div>
      </div>
    </div>
    <div v-if="error" class="error-overlay">
      <div class="error-content">
        <div class="error-icon">⚠️</div>
        <div>加载失败: {{ error }}</div>
        <button @click="retryLoading">重试</button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, watch, onMounted, onBeforeUnmount } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
import { FBXLoader } from 'three/addons/loaders/FBXLoader.js';
import { STLLoader } from 'three/addons/loaders/STLLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';

const props = defineProps({
  modelUrl: {
    type: String,
    required: true
  },
  modelType: {
    type: String,
    required: true,
    validator: (value) => ['gltf', 'glb', 'obj', 'fbx', 'stl'].includes(value)
  },
  backgroundColor: {
    type: String,
    default: '#f0f0f0'
  },
  showAxes: {
    type: Boolean,
    default: true
  },
  dracoDecoderPath: {
    type: String,
    default: 'https://www.gstatic.com/draco/versioned/decoders/1.5.6/'
  }
});

const emit = defineEmits(['loading', 'loaded', 'error', 'progress']);

const container = ref(null);
const loading = ref(false);
const progress = ref(0);
const error = ref(null);

let scene, camera, renderer, controls, currentModel;

// 初始化场景
function initScene() {
  // 创建场景
  scene = new THREE.Scene();
  scene.background = new THREE.Color(props.backgroundColor);
  
  // 创建相机
  camera = new THREE.PerspectiveCamera(
    75,
    container.value.clientWidth / container.value.clientHeight,
    0.1,
    1000
  );
  camera.position.set(5, 5, 5);
  
  // 创建渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(container.value.clientWidth, container.value.clientHeight);
  container.value.appendChild(renderer.domElement);
  
  // 添加轨道控制器
  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;
  controls.dampingFactor = 0.05;
  
  // 添加光源
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
  scene.add(ambientLight);
  
  const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
  directionalLight.position.set(5, 10, 7);
  scene.add(directionalLight);

  // 添加坐标轴辅助
  if (props.showAxes) {
    const axesHelper = new THREE.AxesHelper(5);
    scene.add(axesHelper);
  }
}

// 从网络加载模型
async function loadModelFromUrl() {
  if (!props.modelUrl) return;
  
  loading.value = true;
  error.value = null;
  emit('loading', true);
  
  try {
    // 清除旧模型
    if (currentModel) {
      scene.remove(currentModel);
      currentModel = null;
    }
    
    const response = await fetch(props.modelUrl);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    let data;
    if (props.modelType === 'gltf') {
      data = await response.text();
    } else {
      data = await response.arrayBuffer();
    }
    
    await loadModelData(data);
    
    emit('loaded');
  } catch (err) {
    error.value = err.message || '加载模型失败';
    emit('error', err);
  } finally {
    loading.value = false;
    emit('loading', false);
  }
}

// 加载模型数据
async function loadModelData(data) {
  switch (props.modelType) {
    case 'gltf':
    case 'glb':
      await loadGLTF(data);
      break;
    case 'obj':
      await loadOBJ(data);
      break;
    case 'fbx':
      await loadFBX(data);
      break;
    case 'stl':
      await loadSTL(data);
      break;
    default:
      throw new Error('不支持的模型格式');
  }
}

// 加载GLTF/GLB模型
async function loadGLTF(data) {
  const loader = new GLTFLoader();
  
  // 设置DRACO解码器
  const dracoLoader = new DRACOLoader();
  dracoLoader.setDecoderPath(props.dracoDecoderPath);
  loader.setDRACOLoader(dracoLoader);
  
  let gltf;
  if (typeof data === 'string') {
    // GLTF文本格式
    gltf = await loader.parseAsync(data, '', (res) => {
      const p = (res.loaded / res.total * 100).toFixed(2);
      progress.value = parseFloat(p);
      emit('progress', progress.value);
    });
  } else {
    // GLB二进制格式
    gltf = await loader.parseAsync(data, '', (res) => {
      const p = (res.loaded / res.total * 100).toFixed(2);
      progress.value = parseFloat(p);
      emit('progress', progress.value);
    });
  }
  
  currentModel = gltf.scene;
  scene.add(currentModel);
  centerModel(currentModel);
}

// 加载OBJ模型
async function loadOBJ(data) {
  const loader = new OBJLoader();
  currentModel = loader.parse(data);
  scene.add(currentModel);
  centerModel(currentModel);
}

// 加载FBX模型
async function loadFBX(data) {
  const loader = new FBXLoader();
  currentModel = loader.parse(data);
  scene.add(currentModel);
  centerModel(currentModel);
}

// 加载STL模型
async function loadSTL(data) {
  const loader = new STLLoader();
  const geometry = loader.parse(data);
  
  const material = new THREE.MeshStandardMaterial({
    color: 0xaaaaaa,
    metalness: 0.5,
    roughness: 0.5,
  });
  
  currentModel = new THREE.Mesh(geometry, material);
  scene.add(currentModel);
  centerModel(currentModel);
}

// 居中并缩放模型
function centerModel(model) {
  const box = new THREE.Box3().setFromObject(model);
  const center = box.getCenter(new THREE.Vector3());
  const size = box.getSize(new THREE.Vector3());
  
  // 居中
  model.position.x += (model.position.x - center.x);
  model.position.y += (model.position.y - center.y);
  model.position.z += (model.position.z - center.z);
  
  // 缩放以适应视图
  const maxDim = Math.max(size.x, size.y, size.z);
  const scale = 5 / maxDim;
  model.scale.set(scale, scale, scale);
  
  // 更新相机位置
  camera.position.set(0, 0, 10);
  controls.update();
}

// 处理窗口大小变化
function onWindowResize() {
  camera.aspect = container.value.clientWidth / container.value.clientHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(container.value.clientWidth, container.value.clientHeight);
}

// 重试加载
function retryLoading() {
  loadModelFromUrl();
}

// 动画循环
function animate() {
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);
}

// 监听props变化
watch(() => [props.modelUrl, props.modelType], () => {
  loadModelFromUrl();
});

onMounted(() => {
  initScene();
  animate();
  window.addEventListener('resize', onWindowResize);
  loadModelFromUrl();
});

onBeforeUnmount(() => {
  window.removeEventListener('resize', onWindowResize);
  if (renderer) {
    renderer.dispose();
  }
});
</script>

<style scoped>
.model-container {
  width: 100%;
  height: 100%;
  min-height: 500px;
  position: relative;
}

.loading-overlay, .error-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 100;
}

.loading-content, .error-content {
  background: white;
  padding: 20px;
  border-radius: 8px;
  text-align: center;
}

.spinner {
  border: 4px solid rgba(0, 0, 0, 0.1);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border-left-color: #09f;
  animation: spin 1s linear infinite;
  margin: 0 auto 10px;
}

.error-icon {
  font-size: 36px;
  margin-bottom: 10px;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

button {
  margin-top: 10px;
  padding: 5px 15px;
  background: #09f;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
</style>